package cn.edu.shou.lab.service.impl;

import cn.edu.shou.lab.config.constant.SHOU;
import cn.edu.shou.lab.config.converters.BackResult;
import cn.edu.shou.lab.config.query.Retrieval;
import cn.edu.shou.lab.config.query.Term;
import cn.edu.shou.lab.config.utils.AdvancedSqlBuilder;
import cn.edu.shou.lab.config.utils.ContextUtils;
import cn.edu.shou.lab.config.utils.FormatUtil;
import cn.edu.shou.lab.config.utils.SimpleSqlBuilder;
import cn.edu.shou.lab.service.CohesionServiceI;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.Id;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

@Service
public class CohesionService implements CohesionServiceI {

    private final static Logger LOGGER = Logger.getLogger(CohesionService.class.getName());

    @Autowired
    JdbcTemplate jdbcTemplate;

    private Class<?> clazz ;
    private JpaRepository repo ;
    private Class idType;


    @Override
    @Transactional
    public BackResult executeSaveOrUpdate(String dataTable, List<Map<String, Object>> beans) {
        BackResult result = new BackResult();
        try {
            if (init(dataTable)) {
                Object entity;
                Field field;
                for(Map<String, Object> bean : beans){
                    entity = this.clazz.newInstance();
                    for (Map.Entry<String, Object> entry : bean.entrySet()) {
                        try {
                            field = clazz.getDeclaredField(entry.getKey());
                            field.setAccessible(true);
                            field.set(entity, FormatUtil.getStdReflectValue(field, entry.getValue().toString()));
                        }catch (Exception e){
                            try {
                                field = clazz.getSuperclass().getDeclaredField(entry.getKey());
                                field.setAccessible(true);
                                field.set(entity, FormatUtil.getStdReflectValue(field, entry.getValue().toString()));
                            }catch (Exception ee){
                                LOGGER.warning("未找到相应字段：" + entry.getKey());
                            }
                        }
                    }
                    this.repo.save(entity);
                }
            }else{
                LOGGER.warning("initialize error");
                result.setState(false);
                result.setMsg("CohesionService initialize error");
            }
        } catch (Exception e) {
            LOGGER.warning(e.getMessage());
            result.setState(false);
            result.setMsg(e.getMessage());
        }
        return result;
    }


    @Override
    public List<Map<String, Object>> executeQuery(String dataTable, List<Retrieval> bean, Pageable pageable) {
        String sql = new SimpleSqlBuilder(dataTable, bean, pageable).getSql();
        return jdbcTemplate.queryForList(sql);
    }

    @Override
    public List<Map<String, Object>> executeQuery(String dataTable, Term term, Pageable pageable) {
        String sql = new AdvancedSqlBuilder(dataTable, term, pageable).getSql();
        return jdbcTemplate.queryForList(sql);
    }

    @Override
    public Map<String, Object> getCount(String dataTable, Term term) {
        String sql = new AdvancedSqlBuilder(dataTable, term, null).getCount();
        return jdbcTemplate.queryForList(sql).get(0);
    }

    @Override
    public BackResult executeDelete(String dataTable, List<String> ids) {
        BackResult result = new BackResult();
        try {
            if (init(dataTable)) {
                for (String id : ids) {
                    repo.deleteById(idType.cast(id));
                }
                return result;
            }else{
                LOGGER.warning("initialize error");
                result.setState(false);
                result.setMsg("CohesionService initialize error");
            }
        } catch (Exception e) {
            LOGGER.warning(e.getMessage());
            result.setState(false);
            result.setMsg(e.getMessage());
        }
        return result;
    }
    /**
     * 初始化方法，初始化实体类，对应JPA，主键类型，并检查初始化是否成功
     * @param dataTable 实体名
     * @return 初始化是否成功
     */
    private boolean init(String dataTable){
        try {
            ApplicationContext ctx = ContextUtils.getContext();
            String fullPackName = getFullPackName(dataTable);
            this.repo = (JpaRepository) ctx.getBean(getRepoName(dataTable));
            this.clazz = Class.forName(fullPackName);
            this.idType = getIdType(this.clazz);
            return check();
        } catch (Exception e){
            e.printStackTrace();
        }
        return false;
    }

    private String getRepoName(String dataTable) {
        if (org.springframework.util.StringUtils.isEmpty(dataTable)) {
            throw new RuntimeException("dataTable can't be null or empty.");
        }
        dataTable = dataTable.toLowerCase();
        return dataTable + "Repository";
    }

    private String getFullPackName(String dataTable) {
        if (org.springframework.util.StringUtils.isEmpty(dataTable)) {
            throw new RuntimeException("dataTable can't be null or empty.");
        }
        return SHOU.ENTITY_PATH + "." + dataTable;
    }

    private Class getIdType(Class clazz){
        if (clazz == null) {
            throw new RuntimeException("Can not find the entity.");
        }
        Field[] fields = clazz.getDeclaredFields();
        for(Field field : fields){
            if(field.getAnnotation(Id.class) != null){
                return field.getType();
            }
        }
        fields = clazz.getSuperclass().getDeclaredFields();
        for(Field field : fields){
            if(field.getAnnotation(Id.class) != null){
                return field.getType();
            }
        }
        throw new RuntimeException("The entity [" + this.clazz.getName() + "]  has no primary key.");
    }

    private boolean check(){
        return this.clazz != null && this.repo != null && this.idType != null;
    }

}
